"""Handling asynchronous I/O."""

import os
import select
import threading

class Runner(object):
    def __init__(self):
        self.stdout_dict = {}
        self.stderr_dict = {}
        self.running = False
        self.manager = None

    def set_manager(self, manager):
        """Set manager for the runner."""
        self.manager = manager

    def start(self):
        """Start polling loop in another thread."""
        t = threading.Thread(target=self.run)
        t.start()

    def run(self):
        """Run the polling loop procedure."""
        # Check running first
        self.poller = select.epoll()
        self.rfd, self.wfd = os.pipe()
        self.poller.register(self.rfd, select.EPOLLIN)
        self.running = True
        # Poll loop
        while self.running:
            for fd, event in self.poller.poll():
                if fd == self.rfd and event & select.EPOLLIN:
                    self.handle_pipe()
                elif (event & select.EPOLLIN) or (event & select.EPOLLHUP):
                    self.pass_to_task(fd, event)
                else:
                    # TODO: Handle exception
                    print event
            self.adjust_readable_dict()
        self.poller.close()
        os.close(self.wfd)
        os.close(self.rfd)

    def handle_pipe(self):
        """Handling pipe's content, maybe inserted via wake_on_poller()."""
        # TODO: Check status first
        letter = os.read(self.rfd, 1)
        # TODO: Check whether letter is empty or not
        if letter == '*':
            pass
        elif letter == 'q':
            self.running = False
        else:
            # TODO: Handle exception
            pass

    def pass_to_task(self, fd, event):
        """Pass event to Task's read() or close() functions."""
        if fd in self.stdout_dict:
            if event & select.EPOLLIN:
                self.stdout_dict[fd].handle_read_stdout()
            elif event & select.EPOLLHUP:
                self.stdout_dict[fd].handle_close_stdout()
        elif fd in self.stderr_dict:
            if event & select.EPOLLIN:
                self.stderr_dict[fd].handle_read_stderr()
            elif event & select.EPOLLHUP:
                self.stderr_dict[fd].handle_close_stderr()
        else:
            # TODO: Handle exception
            pass

    def adjust_readable_dict(self):
        """Adjust readable Tasks' stdout and stderr dict."""
        # TODO: Check manager first
        # TODO: Check poller
        for task in self.manager.iterate_tasks():
            if task.stdout_readable():
                stdout_fd = task.get_stdout_fd()
                if not stdout_fd in self.stdout_dict:
                    self.poller.register(stdout_fd, select.EPOLLIN)
                    self.stdout_dict[stdout_fd] = task
            else:
                for fd, t in self.stdout_dict.items():
                    if t.get_id() == task.get_id():
                        self.poller.unregister(fd)
                        del self.stdout_dict[fd]
            if task.stderr_readable():
                stderr_fd = task.get_stderr_fd()
                if not stderr_fd in self.stderr_dict:
                    self.poller.register(stderr_fd, select.EPOLLIN)
                    self.stderr_dict[stderr_fd] = task
            else:
                for fd, t in self.stderr_dict.items():
                    if t.get_id() == task.get_id():
                        self.poller.unregister(fd)
                        del self.stderr_dict[fd]

    def wake_on_poller(self, letter='*'):
        """Wake on poller with specified letter."""
        # TODO: Check running first
        os.write(self.wfd, letter)
